home *** CD-ROM | disk | FTP | other *** search
Wrap
/****************************************************************************** MODUL math.c DESCRIPTION Simple Math commands for DME/XDME (integer only) Variable swapping for DME/XDME NOTES -/- BUGS <none known> TODO erweitertes Math-interfaces (flexibler, mehr funktionen) EXAMPLES [ $a == 10 $b == 11 ] inc a [-> $a == 11] add b ( 3 * 4 + $b ) [-> $b == 34] SEE ALSO vars.c AUTHOR Bernd "0" Noll (b_noll@informatik.uni-kl.de) HISTORY <see RCS-File> 01-10-94 b_noll introduced DEFMESSAGE *!*************************************************************************** *! *! integer MATH Functions *! ******************************************************************************/ /* ** (C)Copyright 1992 by Bernd Noll for null/zero-soft ** All Rights Reserved ** ** RCS Header: $Id: Math.c 1.2 1994/09/20 11:05:32 b_noll Exp b_noll $ ** ** */ /************************************** Includes **************************************/ #include "defs.h" #ifdef PATCH_NULL #include "COM.h" #include "libs/AUTO.h" #endif /************************************** Globale Exports **************************************/ //Prototype char MathInfix; Prototype void do_unnamedmathfunc(void); Prototype void do_namedmathfunc (void); Prototype void do_infixmode (void); Prototype void do_swapV (void); /************************************** Interne Defines & Strukturen **************************************/ /* --- math-function-modi if math_func_helper doesn't use function-parameters */ #define MODE_ADD simplehash('a','d') /* x = x+y (2) */ #define MODE_DEC simplehash('d','e') /* x = x-1 (1) */ #define MODE_DIV simplehash('d','i') /* x = x/y (2) */ #define MODE_INC simplehash('i','n') /* x = x+1 (1) */ #define MODE_MOD simplehash('m','o') /* x = x%y (2) */ #define MODE_MUL simplehash('m','u') /* x = x*y (2) */ #define MODE_NEG simplehash('n','e') /* x = -x (1) */ #define MODE_NOT simplehash('n','o') /* x = ~x (1) */ #define MODE_SUB simplehash('s','u') /* x = x-y (2) */ #ifndef MAXIA #define MAXIA 5 #endif #define OP_SUB 1 /* the values are used for priorities, too */ #define OP_ADD 2 #define OP_DIV 3 #define OP_MOD 4 #define OP_MUL 5 /************************************** Interne Prototypes **************************************/ /************************************** Impementation **************************************/ /* *! *! math functions offer the user a simple calculator, *! which recognizes +,-,*,/,%,(,); *! 2 numbers must be separated with one operator, *! !!each operator must be surrounded with whitespaces!! *! (there are no unique +/- operators allowed, if they are *! not part of a number, so "-1" is allowed, "(- 1)" is not!) *! *! so each expression has an inequal number of subexpressions - *! x+1 values and x operators with x in N *! there should not be more than MAXIA (== 5) *! expressions on one level (simply use brackets) *! ( that is in fact no problem, as on any evaluation *! we try to shorten our results due to operator prorities, *! and as long as there are only 4 operators used, we do *! normally not run out of space *! e.g.:> set abc 0 add abc (1 * 2 + 3 * 4 / 5 - 6) title $abc *! => 1 => 1* => 1*2 => 1*2+ [-> 2+] *! => 2+3 => 2+3* => 2+3*4 => 2+3*4/ [-> 2+12/] *! => 2+12/5 => 2+12/5- [-> 2+2-] [-> 4-] => 4-6 *! == -2 *! */ static long domathop (long a1, char op, long a2) { switch (op) { case OP_ADD: return (a1 + a2); case OP_SUB: return (a1 - a2); case OP_MUL: return (a1 * a2); case OP_DIV: return (a1 / a2); case OP_MOD: return (a1 % a2); default: DEFMESSAGE( _MATH_unknown_operator, "Math:\nunknown operator `%s'" ) error (_MATH_unknown_operator, "?"); return (0); } /* switch */ } /* domathop */ static char getmathop (char * str) { if (!str) { return(0); } /* if */ switch (*str) { case '+': return (OP_ADD); case '-': return (OP_SUB); case '*': return (OP_MUL); case '/': return (OP_DIV); case '%': return (OP_MOD); default: DEFMESSAGE( _MATH_unknown_operator, "Math:\nunknown operator `%s'" ) error (_MATH_unknown_operator, str); return(0); } /* switch */ } /* getmathop */ /* static */ long getmathresult (char* expr) { long values[MAXIA]; char funcs [MAXIA]; char * str = expr; int i; char * checker; char * aux2 = NULL; char * dummy = NULL; char quote = 0; /* --------- erstes argument muss immer existieren */ if (!expr) { DEFMESSAGE( _MATH_empty_expression, "empty expression\n(%sNIL)" ) error (_MATH_empty_expression, "Input "); return(0); } /* if */ if (is_number(expr)) { return(atoi(expr)); } /* if */ checker = (UBYTE *)breakout(&str, "e, &aux2); if (!checker) { #if 0 if (aux2) { free (aux2); aux2 = NULL; } /* if */ /* seems senseless */ #endif DEFMESSAGE( _MATH_empty_expression, "empty expression\n(%sNIL)" ) error (_MATH_empty_expression, "breakout "); return(0); } /* if */ /* PATCH_NULL [07 Apr 1993] : BUGFIX >>> */ /* now we are testing : 1. result word starts w/ alpha, 2. word was NOT multiword (AND is nonnumber), 3. word is NOT quoted (AND is not number) */ if (isalpha(checker[0]) || ((str == NULL || *str == 0) && (quote == 0))) { /* should be better : check up to the first space, if there is a non-digit */ if (aux2) free(aux2); DEFMESSAGE( _MATH_nonnumber, "non-number to math-function" ) error (_MATH_nonnumber); return (0); } /* if */ /* PATCH_NULL [07 Apr 1993] : BUGFIX <<< */ values[0] = getmathresult ( checker ); if (aux2) { free (aux2); aux2 = NULL; } /* if */ i = 0; while (!IS_ABORTED() && str && ( 1/* first checking here */ )) { /* --------- operator holen - better put that line into the while-condition */ checker = (UBYTE *)breakout(&str, (void*)&dummy, &aux2); if (!checker) { #if 0 if (aux2) { free (aux2); aux2 = NULL; } /* if */ /* seems senseless */ #endif goto ende; } else { funcs[i] = getmathop(checker); if (aux2) { free (aux2); aux2 = NULL; } /* if */ if (IS_ABORTED()) { /* printf ("abort unbekannt\n"); */ return (0); } /* if (aborted) */ /* --------- wenn möglich, verkuerzen */ while (i>0 && funcs[i-1] >= funcs[i]) { values[i-1] = domathop(values[i-1], funcs[i-1], values[i]); funcs[i-1] = funcs[i]; i--; } /* while */ i++; if (i >= MAXIA) { DEFMESSAGE( _MATH_expression_too_complex, "Expression too complex:\n%s" ) error (_MATH_expression_too_complex, expr); return (0); } /* if */ /* --------- naechstes argument holen */ checker = (UBYTE *)breakout(&str, (void*)&dummy, &aux2); values[i] = getmathresult(checker); if (aux2) { free (aux2); } /* if */ if (IS_ABORTED()) { return (0); } /* if */ } /* if ex operator */ } /* while not end of expr */ ende: while (i > 0) { values[i-1] = domathop(values[i-1], funcs[i-1], values[i]); i--; } /* while */ /* printf("ok %ld\n", values[0]); */ return (values[0]); } /* getmathresult */ /* ** math_hash() ** Calculate the right math-function defined by a string ** */ static int math_hash (char * str) { switch (str[1]) { /* PATCH_NULL [04 Aug 1993] : BUGFIX */ case '=': switch (str[0]) { case '+': return(MODE_ADD); case '-': return(MODE_SUB); case '*': return(MODE_MUL); case '/': return(MODE_DIV); case '%': return(MODE_MOD); case '~': case '^': return(MODE_NOT); /* nun ja minus statt NOT ... */ } /* switch */ case '-': return(MODE_DEC); case '+': return(MODE_INC); default: return(simplehash(str[0],str[1])); } /* if */ } /* math_hash */ /* *! MATH *! *! math-commands, they are all implemented in do_mathfunc *! *! Very simple syntax: *! *! >[MATH1] NEG / INC / DEC / NOT <varname> *! >[MATH2] ADD / SUB / MUL / DIV / MOD <varname> (<value>) *! *! <value> is a valid dezimal number *! <varname> must be given at each function and must be the name *! of an existing variable containing a valid number *! *! (a simple hack to do some increment etc. without arexx) *! *! (due to less feedback, all the math commands are planned *! to be replaced with one single command MATH name value) *! */ static void do_mathfunc (char* mmode, char* vname, char* mval) { int mathfunc; char str[16]; long l1; long l2; char* sstr; int type; type = VAR_NEX; /* initialise */ str[15] = '\0'; l2 = 0; /* l1 = 0; */ /* --- get the right operation mode */ mathfunc = math_hash(mmode); /* recognize the mathfunction via mmode */ /* --- if necessary and existing, get the 2nd argument */ if ((mathfunc != MODE_NOT)&& (mathfunc != MODE_NEG)&& (mathfunc != MODE_INC)&& (mathfunc != MODE_DEC)) { /* called with 2 arguments => read a number from slot 2 */ if (mval != NULL) { if (is_number(mval)) { l2 = atol(mval); } else { /* Abortcommand = 0; */ DEFMESSAGE( _MATH_invalid_2nd_argument, "%s:\ninvalid 2nd argument %s" ) /* error (_MATH_invalid_2nd_argument, av[0], mval); */ l2 = getmathresult (mval); if (IS_ABORTED()) { return; } /* if */ /* abort2(); */ } /* if */ } else { DEFMESSAGE( _MATH_missing_2nd_operand, "Math:\nMissing 2nd Operand!" ) error (_MATH_missing_2nd_operand); abort2(); } /* if */ } /* if */ /* --- try to get the type and contents of the variable represented by the 1st argument */ if (vname != NULL) { /* get the number in the variable whose name is in slot 1 */ sstr = GetTypedVar(vname,&type); if ((type != VAR_NEX) && is_number(sstr)) { l1 = atol(sstr); if (sstr) free(sstr); } else { if (sstr) free(sstr); DEFMESSAGE( _MATH_invalid_1st_argument, "%s:\ninvalid 1st argument %s" ) error (_MATH_invalid_1st_argument, av[0], vname); abort2(); } /* if */ } else { DEFMESSAGE( _MATH_missing_1st_argument, "%s:\nmissing 1st argument %s" ) error (_MATH_missing_1st_argument, av[0]); abort2(); } /* if */ /* --- do the specified math operation */ switch (mathfunc) { /* apply the math-function to the numbers */ /* it would be finer to use a function-variable - next time perhaps */ case MODE_NEG: l2 = -l1; break; case MODE_NOT: l2 = ~l1; break; case MODE_INC: l2 = l1+1; break; case MODE_DEC: l2 = l1-1; break; case MODE_ADD: l2 = l1+l2; break; case MODE_SUB: l2 = l1-l2; break; case MODE_MUL: l2 = l1*l2; break; case MODE_DIV: if (l2!=0) { l2 = l1/l2; } else { DEFMESSAGE( _MATH_division_by_0, "Math:\nDivision by Zero!" ) error (_MATH_division_by_0); abort2(); } /* if */ break; case MODE_MOD: if (l2!=0) { l2 = l1%l2; } else { DEFMESSAGE( _MATH_division_by_0, "Math:\nDivision by Zero!" ) error (_MATH_division_by_0); abort2(); } /* if */ break; default: DEFMESSAGE( _MATH_unknown_operator, "Math:\nunknown operator `%s'" ) error (_MATH_unknown_operator, "?" ); abort2(); } /* switch */ if ((type == VAR_TF) || (type == VAR_GF) /* || (type == VAR_SF) || (type == VAR_PF) */) /* if dest is flag change to boolean */ l2 = (l2 != 0); /* --- write back the value , if necessary */ if ((l2 != l1)) { /* the function has changed a value => put it back to its variable */ sprintf(str,"%ld",l2); SetTypedVar(vname, str, type); } /* if */ } /* do_mathfunc */ /* *! SMATH Interface: *! *! consisting of 8 short mathfuncs: *! *! >INC/ DEC/ NEG/ NOT x *! >ADD/ SUB/ MUL/ DIV/ MOD x y *! *! which expect 1 or 2 arguments *! their function is recognized with their name *! since they all use the same routines. *! *! (in fact these functions are abbreviations of MATH?) *! */ void do_namedmathfunc (void) { do_mathfunc(av[0], av[1], av[2]); } /* do_namedmathfunc */ /* *! LMATH Interface: *! *! 2 long math-functions: *! *! >MATH1 x y *! >MATH2 x y z *! *! which expect 2 or 3 arguments *! *! If You use the Toggle INFIX *! the second argument defines the mathematical function *! else the first arg does *! *! *! >INFIX bool *! *! define the argument order of the MATH? commands *! with INFIX OFF we say *! % MATH? operator variable [value] *! with INFIX ON we use *! % MATH? variable operator [value] *! */ // char MathInfix = 0; void do_unnamedmathfunc (void) { if (MathInfix) do_mathfunc(av[2], av[1], av[3]); /* infix notation */ else do_mathfunc(av[1], av[2], av[3]); /* prefix notation */ } /* do_unnamedmathfunc */ void do_infixmode (void) { MathInfix = test_arg (av[1], MathInfix); DEFMESSAGE( _MATH_infixmode, "Math Infixmode %s" ) title (_MATH_infixmode, MathInfix? __on: __off); } /* do_infixmode */ /* *! >SWAP var1 var2 *! *! Swap the contents of 2 variables *! *! the command should recognize the type of the 2 variables and *! use the appropriate set-funcions *! */ void do_swapV (void) { char* value[2]; int type[2]; int i; /* --- read values */ for (i = 0; i < 2; i++) { if (!(value[i] = GetTypedVar(av[1+i], &type[i]))) { DEFMESSAGE( _MATH_unknown_variable, "%s:\nUnknown Variable '%s'!" ) error (_MATH_unknown_variable, av[0], av[1+i]); goto abortion; } /* if */ } /* for */ #ifdef N_DEF /* --- check compability */ for (i = 0; i < 2; i++) { switch (type[i]) { case VAR_TF: case VAR_GF: /* case VAR_PF: case VAR_SF: */ dummy = toggler(1,value[1-i]); break; case VAR_NEX: DEFMESSAGE( _MATH_unknown_variable, "%s:\nUnknown Variable '%s'!" ) error (_MATH_unknown_variable, av[0], av[1+i]); goto abortion; } /* switch */ } /* for */ if (aborted) { DEFMESSAGE( _MATH_inkompatible_values, "%s:\nIncompatible values for\n'%s' and '%s'!" ) /* error (_MATH_inkompatible_values, av[0], av[1], av[2]); */ goto abortion; } #endif /* */ /* --- Write & free values */ for (i = 0; i < 2; i++) { SetTypedVar(av[2-i], value[i], type[1-i]); free (value[i]); } /* for */ return; abortion: free(value[0]); free(value[1]); abort2(); } /* do_swapV */ DEFUSERCMD("math1", 2, CF_VWM|CF_ICO|CF_COK, void, do_unnamedmathfunc, (void),;) DEFUSERCMD("math2", 3, CF_VWM|CF_ICO|CF_COK, void, do_unnamedmathfunc, (void),;) DEFUSERCMD("infixmode", 1, CF_VWM|CF_ICO|CF_COK, void, do_infixmode, (void),;) DEFUSERCMD("inc", 1, CF_VWM|CF_ICO|CF_COK, void, do_namedmathfunc, (void),;) DEFUSERCMD("sub", 2, CF_VWM|CF_ICO|CF_COK, void, do_namedmathfunc, (void),;) DEFUSERCMD("add", 2, CF_VWM|CF_ICO|CF_COK, void, do_namedmathfunc, (void),;) DEFUSERCMD("dec", 1, CF_VWM|CF_ICO|CF_COK, void, do_namedmathfunc, (void),;) DEFUSERCMD("div", 2, CF_VWM|CF_ICO|CF_COK, void, do_namedmathfunc, (void),;) DEFUSERCMD("mod", 2, CF_VWM|CF_ICO|CF_COK, void, do_namedmathfunc, (void),;) DEFUSERCMD("mul", 2, CF_VWM|CF_ICO|CF_COK, void, do_namedmathfunc, (void),;) DEFUSERCMD("not", 1, CF_VWM|CF_ICO|CF_COK, void, do_namedmathfunc, (void),;) DEFUSERCMD("neg", 1, CF_VWM|CF_ICO|CF_COK, void, do_namedmathfunc, (void),;) DEFUSERCMD("swapv", 2, CF_VWM|CF_ICO|CF_COK, void, do_swapV, (void),;) /*DEFHELP #cmd math MATH1 arg1 arg2 - long version for NOT INC NEG DEC; $INFIXMODE decides if arg1 or arg2 is operator, the other arg is variablename */ /*DEFHELP #cmd math MATH2 arg1 arg2 arg3 - long version for MUL MOD DIV SUB ADD; $INFIXMODE decides if arg1 or arg2 is operator, the other arg is variablename */ /*DEFHELP #cmd math INC var - increment the value of @{B}var@{UB} */ /*DEFHELP #cmd math DEC var - decrement the value of @{B}var@{UB} */ /*DEFHELP #cmd math NEG var - negate the value of @{B}var@{UB} */ /*DEFHELP #cmd math NOT var - logical not for the value of @{B}var@{UB} */ /*DEFHELP #cmd math ADD var val - add @{B}val@{UB} to the value of @{B}var@{UB} */ /*DEFHELP #cmd math SUB var val - sub @{B}val@{UB} from the value of @{B}var@{UB} */ /*DEFHELP #cmd math MUL var val - multiply the value of @{B}var@{UB} with @{B}val@{UB} */ /*DEFHELP #cmd math MOD var val - modulo divide the value of @{B}var@{UB} with @{B}val@{UB} */ /*DEFHELP #cmd math DIV var val - divide the value of @{B}var@{UB} with @{B}val@{UB} */ /*DEFHELP #cmd var SWAPV var1 var2 - try to swap the contents of 2 variables */ #ifdef STATIC_COM /* PATCH_NULL < 24-06-94 */ DEFFLAG( 93-04-09, MathInfix, 0 ) #endif #ifdef SPC_VAR generic global bit infixmode = %{ reference = "MathInfix"; help = %[ the decision, if MATH1 and MATH2 do use prefixed (e.g.% MATH1 ++ count) or infixed (e.g. MATH2 count += 10) operators. %]; gendate = "01-01-93/1"; initval = "0"; %}; #endif /****************************************************************************** ***** ENDE math.c ******************************************************************************/